import dayjs from "dayjs";
import ImageCompressor from "js-image-compressor";
import { getUploadObjectUrl, initMultiPartUpload, insertAttachmentByObjectName, mergeMultiPartUpload } from "./minio";
import axios, { AxiosResponse } from "axios";
import { SYSTEM_CONFIG } from "@/config/system";

export async function uploadMinoFile(file: File, setProgress: Function) {
  let fileTrans = file;
  if (file.type.indexOf("image/") > -1) {
    let blob: any = await compressionImage(file);
    let zipImage = new window.File([blob], file.name, { type: file.type });
    if (zipImage.size < file.size) fileTrans = zipImage;
  }
  if (isBigFile(fileTrans)) {
    console.log("正在进行大文件分片上传");
    return uploadBigFile(fileTrans, setProgress);
  } else {
    console.log("正在进行小文件上传");
    return uploadSmallFile(fileTrans, setProgress);
  }
}

/**
 * @description: 上传小文件
 * @param {File} file 文件
 * @param {Function} setProgress 进度函数
 */
export function uploadSmallFile(file: File, setProgress: Function) {
  return new Promise(resolve => {
    let objectName = renameFile(file.name);
    getUploadObjectUrl(SYSTEM_CONFIG.MINIO_BUCKET, objectName).then(res => {
      const progressArr = new Array<number>(1);
      uploadPratFile(res.data, file.slice(0, file.size), setProgress, progressArr, 0, file.size).then(res => {
        insertAttachmentByObjectName(SYSTEM_CONFIG.MINIO_BUCKET, objectName, file.name, file.type, file.size).then(res => {
          resolve(res);
        });
      });
    });
  });
}

/**
 * @description: 上传大文件
 * @param {File} file 文件
 * @param {Function} setProgress 进度函数
 */
export function uploadBigFile(file: File, setProgress: Function) {
  return new Promise(resolve => {
    const chunks = createFileChunk(file);
    let originalName = file.name;
    let objectName = renameFile(originalName);
    initMultiPartUpload(SYSTEM_CONFIG.MINIO_BUCKET, objectName, chunks.length).then(initRes => {
      //组织分片上传数据
      const { uploadId, uploadUrls } = initRes.data;
      uploadPartByUploadUrls(uploadUrls, chunks, setProgress, file.size).then(res => {
        mergeMultiPartUpload(
          SYSTEM_CONFIG.MINIO_BUCKET,
          uploadId,
          objectName,
          chunks.length,
          originalName,
          file.type || "none",
          file.size
        ).then(res => {
          resolve(res);
        });
      });
    });
  });
}

/**
 * @description: 批量上传文件
 * @param {string} uploadUrls 上传的url数组
 * @param {Blob} chunks 分片的文件数组
 * @param {Function} setProgress 进度函数
 * @param {number} fileSize 文件大小
 */
export function uploadPartByUploadUrls(uploadUrls: string[], chunks: Blob[], setProgress: Function, fileSize: number) {
  //如果上传的url与拆分的文件个数不一致 代表分片出了问题 请用户重试
  return new Promise((resolve, reject) => {
    if (uploadUrls.length !== chunks.length) {
      reject("文件分片失败 请重新上传");
      return;
    }
    const arr = [] as Promise<AxiosResponse<any>>[];
    const progressArr = new Array<number>(uploadUrls.length);
    for (let i = 0; i < uploadUrls.length; i++) {
      let promise = uploadPratFile(uploadUrls[i], chunks[i], setProgress, progressArr, i, fileSize);
      arr.push(promise);
    }
    axios.all(arr).then(
      axios.spread((...res) => {
        if (res.length === chunks.length) {
          resolve(true);
        } else {
          reject("文件分片上传错误 数据丢失 请重新上传");
        }
      })
    );
  });
}

/**
 * @description: 上传片段文件
 * @param {string} url 上传地址
 * @param {Blob} blob 文件
 * @param {Function} setProgress 进度函数
 * @param {Array} progressArr 地址数组
 * @param {number} index 索引
 * @param {number} fileSumSize 文件大小
 */
export function uploadPratFile(
  url: string,
  blob: Blob,
  setProgress: Function,
  progressArr: Array<number>,
  index: number,
  fileSumSize: number
) {
  return axios.put(url, blob, {
    headers: { "Content-Type": "application/octet-stream" },
    onUploadProgress: function (progressEvent) {
      //原生获取上传进度的事件
      if (progressEvent.lengthComputable && setProgress && progressArr && index != undefined && fileSumSize) {
        progressArr[index] = progressEvent.loaded;
        loadProgress(progressArr, fileSumSize, setProgress);
      }
    }
  });
}

/**
 * @description: 进度函数
 * @param {Array} progressArr 地址数组
 * @param {number} fileSumSize 文件大小
 * @param {Function} setProgress 进度函数
 */
function loadProgress(progressArr: Array<number>, fileSumSize: number, setProgress: Function) {
  let loadedSum = 0;
  progressArr.forEach(loaded => {
    loadedSum += loaded;
  });
  setProgress(loadedSum, fileSumSize);
}

/**
 * 压缩图片
 * @param file
 * @param callback
 */
function compressionImage(file: File) {
  return new Promise((resolve, reject) => {
    return new ImageCompressor({
      file: file,
      quality: 0.4,
      redressOrientation: false,
      // 压缩前回调
      beforeCompress: function (result: any) {
        console.log("压缩之前图片尺寸大小: ", result.size);
      },
      success: function (result: any) {
        console.log("压缩之后图片尺寸大小: ", result.size);
        resolve(result);
      },
      error(e: any) {
        reject(e);
      }
    });
  });
}

/**
 * 是否为大文件
 * @param file 文件
 */
export function isBigFile(file: File) {
  return file.size > SYSTEM_CONFIG.MINIO_BIG_FILE_SIZE;
}

/**
 * 重命名文件
 */
function renameFile(fileName: string) {
  //生成随机数
  let random = Math.ceil(Math.random() * 1000000);
  //生成当前文件夹
  let date = new Date();
  let year = dayjs(date).format("YYYY");
  let yearMonth = dayjs(date).format("YYYY-MM");
  let yearMonthDay = dayjs(date).format("YYYY-MM-DD");
  let time = dayjs(date).format("YYYYMMDDHHmmssSSS");
  let fileSuffix = fileName.substring(fileName.lastIndexOf("."));
  let newName = year + "/" + yearMonth + "/" + yearMonthDay + "/" + time + "_" + random + fileSuffix;
  return newName;
}

/**
 * 将文件进行分片上传
 * @param file 文件
 * @param size 分片文件大小
 * @returns
 */
export function createFileChunk(file: File, size = SYSTEM_CONFIG.MINIO_BIG_FILE_SIZE) {
  const fileChunkList: Blob[] = [];
  let count = 0;
  while (count < file.size) {
    fileChunkList.push(file.slice(count, count + size));
    count += size;
  }
  return fileChunkList;
}
